<!DOCTYPE html><html>

<head>
<meta charset="utf-8">
<title>LTS文档</title>
<style type="text/css">
body {
  font-family: Helvetica, arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: white;
  padding: 30px; }

body > *:first-child {
  margin-top: 0 !important; }
body > *:last-child {
  margin-bottom: 0 !important; }

a {
  color: #4183C4; }
a.absent {
  color: #cc0000; }
a.anchor {
  display: block;
  padding-left: 30px;
  margin-left: -30px;
  cursor: pointer;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0; }

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
  cursor: text;
  position: relative; }

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
  background: url() no-repeat 10px center;
  text-decoration: none; }

h1 tt, h1 code {
  font-size: inherit; }

h2 tt, h2 code {
  font-size: inherit; }

h3 tt, h3 code {
  font-size: inherit; }

h4 tt, h4 code {
  font-size: inherit; }

h5 tt, h5 code {
  font-size: inherit; }

h6 tt, h6 code {
  font-size: inherit; }

h1 {
  font-size: 28px;
  color: black; }

h2 {
  font-size: 24px;
  border-bottom: 1px solid #cccccc;
  color: black; }

h3 {
  font-size: 18px; }

h4 {
  font-size: 16px; }

h5 {
  font-size: 14px; }

h6 {
  color: #777777;
  font-size: 14px; }

p, blockquote, ul, ol, dl, li, table, pre {
  margin: 15px 0; }

hr {
  background: transparent url() repeat-x 0 0;
  border: 0 none;
  color: #cccccc;
  height: 4px;
  padding: 0;
}

body > h2:first-child {
  margin-top: 0;
  padding-top: 0; }
body > h1:first-child {
  margin-top: 0;
  padding-top: 0; }
  body > h1:first-child + h2 {
    margin-top: 0;
    padding-top: 0; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
  margin-top: 0;
  padding-top: 0; }

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0; }

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
  margin-top: 0; }

li p.first {
  display: inline-block; }
li {
  margin: 0; }
ul, ol {
  padding-left: 30px; }

ul :first-child, ol :first-child {
  margin-top: 0; }

dl {
  padding: 0; }
  dl dt {
    font-size: 14px;
    font-weight: bold;
    font-style: italic;
    padding: 0;
    margin: 15px 0 5px; }
    dl dt:first-child {
      padding: 0; }
    dl dt > :first-child {
      margin-top: 0; }
    dl dt > :last-child {
      margin-bottom: 0; }
  dl dd {
    margin: 0 0 15px;
    padding: 0 15px; }
    dl dd > :first-child {
      margin-top: 0; }
    dl dd > :last-child {
      margin-bottom: 0; }

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777; }
  blockquote > :first-child {
    margin-top: 0; }
  blockquote > :last-child {
    margin-bottom: 0; }

table {
  padding: 0;border-collapse: collapse; }
  table tr {
    border-top: 1px solid #cccccc;
    background-color: white;
    margin: 0;
    padding: 0; }
    table tr:nth-child(2n) {
      background-color: #f8f8f8; }
    table tr th {
      font-weight: bold;
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr td {
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr th :first-child, table tr td :first-child {
      margin-top: 0; }
    table tr th :last-child, table tr td :last-child {
      margin-bottom: 0; }

img {
  max-width: 100%; }

span.frame {
  display: block;
  overflow: hidden; }
  span.frame > span {
    border: 1px solid #dddddd;
    display: block;
    float: left;
    overflow: hidden;
    margin: 13px 0 0;
    padding: 7px;
    width: auto; }
  span.frame span img {
    display: block;
    float: left; }
  span.frame span span {
    clear: both;
    color: #333333;
    display: block;
    padding: 5px 0 0; }
span.align-center {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-center > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: center; }
  span.align-center span img {
    margin: 0 auto;
    text-align: center; }
span.align-right {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-right > span {
    display: block;
    overflow: hidden;
    margin: 13px 0 0;
    text-align: right; }
  span.align-right span img {
    margin: 0;
    text-align: right; }
span.float-left {
  display: block;
  margin-right: 13px;
  overflow: hidden;
  float: left; }
  span.float-left span {
    margin: 13px 0 0; }
span.float-right {
  display: block;
  margin-left: 13px;
  overflow: hidden;
  float: right; }
  span.float-right > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: right; }

code, tt {
  margin: 0 2px;
  padding: 0 5px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px; }

pre code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent; }

.highlight pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }

pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }
  pre code, pre tt {
    background-color: transparent;
    border: none; }

sup {
    font-size: 0.83em;
    vertical-align: super;
    line-height: 0;
}
* {
	-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:0 auto;
    }
}
@media print {
	table, pre {
		page-break-inside: avoid;
	}
	pre {
		word-wrap: break-word;
	}
}
</style>
</head>
<body>
<h1 id="toc_0">LTS用户文档</h1>

<p>LTS(light-task-scheduler)主要用于解决分布式任务调度问题，支持实时任务，定时任务和Cron任务。有较好的伸缩性，扩展性，健壮稳定性而被多家公司使用，同时也希望开源爱好者一起贡献。</p>

<h2 id="toc_1">项目地址</h2>

<p>github地址:
<a href="https://github.com/qq254963746/light-task-scheduler">https://github.com/qq254963746/light-task-scheduler</a></p>

<p>oschina地址:
<a href="git.oschina.net/hugui/light-task-scheduler">git.oschina.net/hugui/light-task-scheduler</a></p>

<p>这两个地址都会同步更新。</p>

<h2 id="toc_2">框架概况</h2>

<p>LTS 有主要有以下四种节点：</p>

<ul>
<li>JobClient：主要负责提交任务, 并接收任务执行反馈结果。</li>
<li>JobTracker：负责接收并分配任务，任务调度。</li>
<li>TaskTracker：负责执行任务，执行完反馈给JobTracker。</li>
<li>LTS-Admin：（管理后台）主要负责节点管理，任务队列管理，监控管理等。</li>
</ul>

<p>其中JobClinet，JobTracker，TaskTracker节点都是<code>无状态</code>的。
可以部署多个并动态的进行删减，来实现负载均衡，实现更大的负载量, 并且框架采用FailStore策略使LTS具有很好的容错能力。 </p>

<p>LTS注册中心提供多种实现（Zookeeper，redis等），注册中心进行节点信息暴露，master选举。(Mongo or Mysql)存储任务队列和任务执行日志, netty做底层通信。</p>

<p>LTS支持任务类型：</p>

<ul>
<li>实时任务：提交了之后立即就要执行的任务。</li>
<li>定时任务：在指定时间点执行的任务，譬如 今天3点执行（单次）。</li>
<li>Cron任务：CronExpression，和quartz类似（但是不是使用quartz实现的）譬如 0 0/1 * * * ?</li>
</ul>

<h2 id="toc_3">架构图</h2>

<p><img src="http://git.oschina.net/hugui/light-task-scheduler/raw/master/docs/LTS_architecture.png?dir=0&amp;filepath=docs%2FLTS_architecture.png&amp;oid=1e5daa62b8d032daaa47eab4a84ab1d4c8962c33&amp;sha=774aa73d186470aedbb8f4da3c04a86a6022be05" alt="LTS architecture"></p>

<h2 id="toc_4">概念说明</h2>

<h3 id="toc_5">节点组</h3>

<ol>
<li>英文名称 NodeGroup,一个节点组等同于一个小的集群，同一个节点组中的各个节点是对等的，等效的，对外提供相同的服务。</li>
<li>没个节点组中都有一个master节点，这个master节点是由LTS动态选出来的，当一个master节点挂掉之后，LTS会立马选出另外一个master节点，框架提供API监听接口给用户。</li>
</ol>

<h3 id="toc_6">FailStore</h3>

<ol>
<li>顾名思义，这个主要是用于失败了存储的，主要用于节点容错，当远程数据交互失败之后，存储在本地，等待远程通信恢复的时候，再将数据提交。</li>
<li>FailStore主要用户JobClient的任务提交，TaskTracker的任务反馈，TaskTracker的业务日志传输的场景下。</li>
<li>FailStore目前提供三种实现：leveldb，rocksdb，berkeleydb，用于可以自由选择使用哪种。</li>
</ol>

<h2 id="toc_7">流程图</h2>

<p>下图是一个标准的实时任务执行流程。</p>

<p><img src="http://git.oschina.net/hugui/light-task-scheduler/raw/master/docs/LTS_progress.png?dir=0&amp;filepath=docs%2FLTS_progress.png&amp;oid=22f60a83b51b26bac8dabbb5053ec9913cefc45c&amp;sha=774aa73d186470aedbb8f4da3c04a86a6022be05" alt="LTS progress"></p>

<h2 id="toc_8">特性</h2>

<h3 id="toc_9">1、Spring支持</h3>

<p>LTS可以完全不用Spring框架，但是考虑到很用用户项目中都是用了Spring框架，所以LTS也提供了对Spring的支持，包括Xml和注解，引入<code>lts-spring.jar</code>即可。</p>

<h3 id="toc_10">2、业务日志记录器</h3>

<p>在TaskTracker端提供了业务日志记录器，供应用程序使用，通过这个业务日志器，可以将业务日志提交到JobTracker，这些业务日志可以通过任务ID串联起来，可以在LTS-Admin中实时查看任务的执行进度。</p>

<h3 id="toc_11">3、SPI扩展支持</h3>

<p>SPI扩展可以达到零侵入，只需要实现相应的接口，并实现即可被LTS使用，目前开放出来的扩展接口有</p>

<ol>
<li>对任务队列的扩展，用户可以不选择使用mysql或者mongo作为队列存储，也可以自己实现。</li>
<li>对业务日志记录器的扩展，目前主要支持console，mysql，mongo，用户也可以通过扩展选择往其他地方输送日志。</li>
</ol>

<h3 id="toc_12">4、故障转移</h3>

<p>当正在执行任务的TaskTracker宕机之后，JobTracker会立马分配在宕机的TaskTracker的所有任务再分配给其他正常的TaskTracker节点执行。</p>

<h3 id="toc_13">5、节点监控</h3>

<p>可以对JobTracker，TaskTracker节点进行资源监控，任务监控等，可以实时的在LTS-Admin管理后台查看，进而进行合理的资源调配。</p>

<h3 id="toc_14">6、多样化任务执行结果支持</h3>

<p>LTS框架提供四种执行结果支持，<code>EXECUTE_SUCCESS</code>，<code>EXECUTE_FAILED</code>，<code>EXECUTE_LATER</code>，<code>EXECUTE_EXCEPTION</code>，并对每种结果采取相应的处理机制，譬如重试。</p>

<ul>
<li>EXECUTE_SUCCESS: 执行成功,这种情况，直接反馈客户端（如果任务被设置了要反馈给客户端）。</li>
<li>EXECUTE_FAILED：执行失败，这种情况，直接反馈给客户端，不进行重试。</li>
<li>EXECUTE_LATER：稍后执行（需要重试），这种情况，不反馈客户端，重试策略采用1min，2min，3min的策略，默认最大重试次数为10次，用户可以通过参数设置修改这个重试次数。</li>
<li>EXECUTE_EXCEPTION：执行异常, 这中情况也会重试(重试策略，同上)</li>
</ul>

<h3 id="toc_15">7、FailStore容错</h3>

<p>采用FailStore机制来进行节点容错，Fail And Store，不会因为远程通信的不稳定性而影响当前应用的运行。具体FailStore说明，请参考概念说明中的FailStore说明。</p>

<h2 id="toc_16">项目编译打包</h2>

<p>项目主要采用maven进行构建，目前提供shell脚本的打包。
环境依赖：<code>Java(jdk1.7)</code> <code>Maven</code></p>

<p>用户使用一般分为两种：</p>

<h3 id="toc_17">1、Maven构建</h3>

<p>可以通过maven命令将lts的jar包上传到本地仓库中。在父pom.xml中添加相应的repository，并用deploy命令上传即可。具体引用方式可以参考lts中的例子即可。</p>

<h3 id="toc_18">2、直接Jar引用</h3>

<p>需要将lts的各个模块打包成单独的jar包，并且将所有lts依赖包引入。具体引用哪些jar包可以参考lts中的例子即可。</p>

<h2 id="toc_19">JobTracker和LTS-Admin部署</h2>

<p>提供<code>(cmd)windows</code>和<code>(shell)linux</code>两种版本脚本来进行编译和部署:</p>

<p>1、运行根目录下的<code>sh build.sh</code>或<code>build.cmd</code>脚本，会在<code>dist</code>目录下生成<code>lts-{version}-bin</code>文件夹
2、下面是其目录结构，其中bin目录主要是JobTracker和LTS-Admin的启动脚本。<code>conf</code>目录是JobTracker的配置文件，lib是JobTracker需要使用到的jar包，<code>war</code>是LTS-Admin相关的war包和配置文件。
lts-{version}-bin的文件结构</p>

<pre><code class="language-java">├── bin
│   ├── jobtracker.cmd
│   ├── jobtracker.sh
│   ├── lts-admin.cmd
│   └── lts-admin.sh
├── conf
│   └── zoo
│       ├── jobtracker.cfg
│       └── log4j.properties
├── lib
└── war
    ├── conf
    │   ├── log4j.properties
    │   └── lts-admin.cfg
    ├── lib
    └── lts-admin.war</code></pre>

<p>3、JobTracker启动。如果你想启动一个节点，直接修改下<code>conf/zoo</code>下的配置文件，然后运行 <code>sh jobtracker.sh zoo start</code>即可，如果你想启动两个JobTracker节点，那么你需要拷贝一份zoo,譬如命名为<code>zoo2</code>,修改下<code>zoo2</code>下的配置文件，然后运行<code>sh jobtracker.sh zoo2 start</code>即可。logs文件夹下生成<code>jobtracker-zoo.out</code>日志。<br>
4、LTS-Admin启动.修改<code>war/conf</code>下的配置，然后运行<code>bin</code>下的<code>sh lts-admin.sh</code>或<code>lts-admin.cmd</code>脚本即可。logs文件夹下会生成<code>lts-admin.out</code>日志，启动成功在日志中会打印出访问地址，用户可以通过这个访问地址访问了。</p>

<h2 id="toc_20">JobClient（部署）使用</h2>

<p>需要引入lts的jar包有<code>lts-jobclient-{version}.jar</code>，<code>lts-core-{version}.jar</code> 及其它第三方依赖jar。</p>

<h3 id="toc_21">API方式启动</h3>

<pre><code class="language-java">JobClient jobClient = new RetryJobClient();
jobClient.setNodeGroup(&quot;test_jobClient&quot;);
jobClient.setRegistryAddress(&quot;zookeeper://127.0.0.1:2181&quot;);
jobClient.start();

// 提交任务
Job job = new Job();
job.setTaskId(&quot;3213213123&quot;);
job.setParam(&quot;shopId&quot;, &quot;11111&quot;);
job.setTaskTrackerNodeGroup(&quot;test_trade_TaskTracker&quot;);
// job.setCronExpression(&quot;0 0/1 * * * ?&quot;);  // 支持 cronExpression表达式
// job.setTriggerTime(new Date()); // 支持指定时间执行
Response response = jobClient.submitJob(job);</code></pre>

<h3 id="toc_22">Spring XML方式启动</h3>

<pre><code class="language-java">&lt;bean id=&quot;jobClient&quot; class=&quot;com.lts.spring.JobClientFactoryBean&quot;&gt;
    &lt;property name=&quot;clusterName&quot; value=&quot;test_cluster&quot;/&gt;
    &lt;property name=&quot;registryAddress&quot; value=&quot;zookeeper://127.0.0.1:2181&quot;/&gt;
    &lt;property name=&quot;nodeGroup&quot; value=&quot;test_jobClient&quot;/&gt;
    &lt;property name=&quot;masterChangeListeners&quot;&gt;
        &lt;list&gt;
            &lt;bean class=&quot;com.lts.example.support.MasterChangeListenerImpl&quot;/&gt;
        &lt;/list&gt;
    &lt;/property&gt;
    &lt;property name=&quot;jobFinishedHandler&quot;&gt;
        &lt;bean class=&quot;com.lts.example.support.JobFinishedHandlerImpl&quot;/&gt;
    &lt;/property&gt;
    &lt;property name=&quot;configs&quot;&gt;
        &lt;props&gt;
            &lt;!-- 参数 --&gt;
            &lt;prop key=&quot;job.fail.store&quot;&gt;leveldb&lt;/prop&gt;
        &lt;/props&gt;
    &lt;/property&gt;
&lt;/bean&gt;</code></pre>

<h3 id="toc_23">Spring 全注解方式</h3>

<pre><code class="language-java">@Configuration
public class LTSSpringConfig {

    @Bean(name = &quot;jobClient&quot;)
    public JobClient getJobClient() throws Exception {
        JobClientFactoryBean factoryBean = new JobClientFactoryBean();
        factoryBean.setClusterName(&quot;test_cluster&quot;);
        factoryBean.setRegistryAddress(&quot;zookeeper://127.0.0.1:2181&quot;);
        factoryBean.setNodeGroup(&quot;test_jobClient&quot;);
        factoryBean.setMasterChangeListeners(new MasterChangeListener[]{
                new MasterChangeListenerImpl()
        });
        Properties configs = new Properties();
        configs.setProperty(&quot;job.fail.store&quot;, &quot;leveldb&quot;);
        factoryBean.setConfigs(configs);
        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }
}</code></pre>

<h2 id="toc_24">TaskTracker(部署使用)</h2>

<p>需要引入lts的jar包有<code>lts-tasktracker-{version}.jar</code>，<code>lts-core-{version}.jar</code> 及其它第三方依赖jar。</p>

<h3 id="toc_25">定义自己的任务执行类</h3>

<pre><code class="language-java">public class MyJobRunner implements JobRunner {
    private final static BizLogger bizLogger = LtsLoggerFactory.getBizLogger();
    @Override
    public Result run(Job job) throws Throwable {
        try {
            // TODO 业务逻辑
            // 会发送到 LTS (JobTracker上)
            bizLogger.info(&quot;测试，业务日志啊啊啊啊啊&quot;);

        } catch (Exception e) {
            return new Result(Action.EXECUTE_FAILED, e.getMessage());
        }
        return new Result(Action.EXECUTE_SUCCESS, &quot;执行成功了，哈哈&quot;);
    }
}</code></pre>

<h3 id="toc_26">API方式启动</h3>

<pre><code class="language-java">TaskTracker taskTracker = new TaskTracker();
taskTracker.setJobRunnerClass(MyJobRunner.class);
taskTracker.setRegistryAddress(&quot;zookeeper://127.0.0.1:2181&quot;);
taskTracker.setNodeGroup(&quot;test_trade_TaskTracker&quot;);
taskTracker.setWorkThreads(20);
taskTracker.start();</code></pre>

<h3 id="toc_27">Spring XML方式启动</h3>

<pre><code class="language-java">&lt;bean id=&quot;taskTracker&quot; class=&quot;com.lts.spring.TaskTrackerAnnotationFactoryBean&quot; init-method=&quot;start&quot;&gt;
    &lt;property name=&quot;jobRunnerClass&quot; value=&quot;com.lts.example.support.MyJobRunner&quot;/&gt;
    &lt;property name=&quot;bizLoggerLevel&quot; value=&quot;INFO&quot;/&gt;
    &lt;property name=&quot;clusterName&quot; value=&quot;test_cluster&quot;/&gt;
    &lt;property name=&quot;registryAddress&quot; value=&quot;zookeeper://127.0.0.1:2181&quot;/&gt;
    &lt;property name=&quot;nodeGroup&quot; value=&quot;test_trade_TaskTracker&quot;/&gt;
    &lt;property name=&quot;workThreads&quot; value=&quot;20&quot;/&gt;
    &lt;property name=&quot;masterChangeListeners&quot;&gt;
        &lt;list&gt;
            &lt;bean class=&quot;com.lts.example.support.MasterChangeListenerImpl&quot;/&gt;
        &lt;/list&gt;
    &lt;/property&gt;
    &lt;property name=&quot;configs&quot;&gt;
        &lt;props&gt;
            &lt;prop key=&quot;job.fail.store&quot;&gt;leveldb&lt;/prop&gt;
        &lt;/props&gt;
    &lt;/property&gt;
&lt;/bean&gt;</code></pre>

<h3 id="toc_28">Spring注解方式启动</h3>

<pre><code class="language-java">@Configuration
public class LTSSpringConfig implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    @Bean(name = &quot;taskTracker&quot;)
    public TaskTracker getTaskTracker() throws Exception {
        TaskTrackerAnnotationFactoryBean factoryBean = new TaskTrackerAnnotationFactoryBean();
        factoryBean.setApplicationContext(applicationContext);
        factoryBean.setClusterName(&quot;test_cluster&quot;);
        factoryBean.setJobRunnerClass(MyJobRunner.class);
        factoryBean.setNodeGroup(&quot;test_trade_TaskTracker&quot;);
        factoryBean.setBizLoggerLevel(&quot;INFO&quot;);
        factoryBean.setRegistryAddress(&quot;zookeeper://127.0.0.1:2181&quot;);
        factoryBean.setMasterChangeListeners(new MasterChangeListener[]{
                new MasterChangeListenerImpl()
        });
        factoryBean.setWorkThreads(20);
        Properties configs = new Properties();
        configs.setProperty(&quot;job.fail.store&quot;, &quot;leveldb&quot;);
        factoryBean.setConfigs(configs);

        factoryBean.afterPropertiesSet();
//        factoryBean.start();
        return factoryBean.getObject();
    }
}</code></pre>

<h2 id="toc_29">参数说明</h2>

<table>
<thead>
<tr>
<th style="text-align: left">参数</th>
<th style="text-align: left">是否必须</th>
<th style="text-align: center">默认值</th>
<th style="text-align: center">使用范围</th>
<th style="text-align: right">设置方式</th>
<th style="text-align: right">参数说明</th>
</tr>
</thead>

<tbody>
<tr>
<td style="text-align: left">registryAddress</td>
<td style="text-align: left">必须</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobClient,JobTracker,TaskTracker</td>
<td style="text-align: right">setRegistryAddress(&quot;xxxx&quot;)</td>
<td style="text-align: right">注册中心，可以选用zk或者redis，参考值: zookeeper://127.0.0.1:2181</td>
</tr>
<tr>
<td style="text-align: left">clusterName</td>
<td style="text-align: left">必须</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobClient,JobTracker,TaskTracker</td>
<td style="text-align: right">setClusterName(&quot;xxxx&quot;)</td>
<td style="text-align: right">集群名称，clusterName相同的所有节点才会组成整个LTS架构</td>
</tr>
<tr>
<td style="text-align: left">listenPort</td>
<td style="text-align: left">必须</td>
<td style="text-align: center">35001</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">setListenPort(xxx)</td>
<td style="text-align: right">JobTracker的远程监听端口</td>
</tr>
<tr>
<td style="text-align: left">job.logger</td>
<td style="text-align: left">必须</td>
<td style="text-align: center">console</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;job.logger&quot;,&quot;xxx&quot;)</td>
<td style="text-align: right">LTS业务日志记录器，可选值console,mysql,mongo,或者自己实现SPI扩展</td>
</tr>
<tr>
<td style="text-align: left">job.queue</td>
<td style="text-align: left">必须</td>
<td style="text-align: center">mongo</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;job.queue&quot;, &quot;xx&quot;)</td>
<td style="text-align: right">LTS任务队列,可选值mongo,mysql,或者自己实现SPI扩展</td>
</tr>
<tr>
<td style="text-align: left">jdbc.url</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;jdbc.url&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">mysql连接URL，当job.queue为mysql的时候起作用</td>
</tr>
<tr>
<td style="text-align: left">jdbc.username</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;jdbc.username&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">mysql连接密码,当job.queue为mysql的时候起作用</td>
</tr>
<tr>
<td style="text-align: left">jdbc.password</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;jdbc.password&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">mysql连接密码,当job.queue为mysql的时候起作用</td>
</tr>
<tr>
<td style="text-align: left">mongo.addresses</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;mongo.addresses&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">mongo连接URL,当job.queue为mongo的时候起作用</td>
</tr>
<tr>
<td style="text-align: left">mongo.database</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">无</td>
<td style="text-align: center">JobTracker</td>
<td style="text-align: right">addConfig(&quot;mongo.database&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">mongo数据库名,当job.queue为mongo的时候起作用</td>
</tr>
<tr>
<td style="text-align: left">zk.client</td>
<td style="text-align: left">可选</td>
<td style="text-align: center">zkclient</td>
<td style="text-align: center">JobClient,JobTracker,TaskTracker</td>
<td style="text-align: right">addConfig(&quot;zk.client&quot;, &quot;xxx&quot;)</td>
<td style="text-align: right">zookeeper客户端,可选值zkclient, curator</td>
</tr>
</tbody>
</table>

<h2 id="toc_30">使用建议</h2>

<p>一般在一个JVM中只需要一个JobClient实例即可，不要为每种任务都新建一个JobClient实例，这样会大大的浪费资源，因为一个JobClient可以提交多种任务。相同的一个JVM一般也尽量保持只有一个TaskTracker实例即可，多了就可能造成资源浪费。当遇到一个TaskTracker要运行多种任务的时候，请参考下面的 &quot;一个TaskTracker执行多种任务&quot;。</p>

<h2 id="toc_31">一个TaskTracker执行多种任务</h2>

<p>有的时候，业务场景需要执行多种任务，有些人会问，是不是要每种任务类型都要一个TaskTracker去执行。我的答案是否定的，如果在一个JVM中，最好使用一个TaskTracker去运行多种任务，因为一个JVM中使用多个TaskTracker实例比较浪费资源（当然当你某种任务量比较多的时候，可以将这个任务单独使用一个TaskTracker节点来执行）。那么怎么才能实现一个TaskTracker执行多种任务呢。下面是我给出来的参考例子。</p>

<pre><code class="language-java">/**
 * 总入口，在 taskTracker.setJobRunnerClass(JobRunnerDispatcher.class)
 * JobClient 提交 任务时指定 Job 类型  job.setParam(&quot;type&quot;, &quot;aType&quot;)
 */
public class JobRunnerDispatcher implements JobRunner {

    private static final ConcurrentHashMap&lt;String/*type*/, JobRunner&gt;
            JOB_RUNNER_MAP = new ConcurrentHashMap&lt;String, JobRunner&gt;();

    static {
        JOB_RUNNER_MAP.put(&quot;aType&quot;, new JobRunnerA()); // 也可以从Spring中拿
        JOB_RUNNER_MAP.put(&quot;bType&quot;, new JobRunnerB());
    }

    @Override
    public Result run(Job job) throws Throwable {
        String type = job.getParam(&quot;type&quot;);
        return JOB_RUNNER_MAP.get(type).run(job);
    }
}

class JobRunnerA implements JobRunner {
    @Override
    public Result run(Job job) throws Throwable {
        //  TODO A类型Job的逻辑
        return null;
    }
}

class JobRunnerB implements JobRunner {
    @Override
    public Result run(Job job) throws Throwable {
        // TODO B类型Job的逻辑
        return null;
    }
}</code></pre>

<h2 id="toc_32">SPI扩展说明</h2>

<h3 id="toc_33">LTS-Logger扩展</h3>

<ol>
<li>引入<code>lts-logger-api-{version}.jar</code></li>
<li>实现<code>JobLogger</code>和<code>JobLoggerFactory</code>接口</li>
<li>在 resources <code>META-INF/lts/com.lts.biz.logger.JobLoggerFactory</code>文件,文件内容为<code>xxx=com.lts.biz.logger.xxx.XxxJobLoggerFactory</code></li>
<li>使用自己的logger扩展，修改jobtracker参数配置 configs.job.logger=xxx。（如果你自己引入JobTracker jar包的方式的话，使用 <code>jobtracker.addConfig(&quot;job.logger&quot;, &quot;xxx&quot;))</code></li>
</ol>

<h3 id="toc_34">LTS-Queue扩展</h3>

<p>实现方式和LTS-Logger扩展类似，具体参考<code>lts-queue-mysql</code>或<code>lts-queue-mongo</code>模块的实现</p>

<h2 id="toc_35">和其它解决方案比较</h2>

<h3 id="toc_36">和MQ比较</h3>

<h3 id="toc_37">和Quartz比较</h3>


</body>

</html>
